

package poker.game;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

/**
 * NOTE THIS CLASS HAS ERRORS DO NOT USE!!!
 *
 * @author Benjamin L. Brodie <blbrodie@gmail.com>
 */
public class FiveCardHand implements Hand {

   

    /**
     * Note: compareTo is inconsistent with equals
     * @param ph a poker hand
     * @return -1 if <, 0 if ==, 1 if >
     */
    public int compareTo(Hand other) {
        Iterator<Integer> it = handEval.listIterator();
        Iterator<Integer> ot = other.getEval().listIterator();
        
        while (it.hasNext() && ot.hasNext()){
            int itVal = it.next();
            int otVal = ot.next();

            if (itVal > otVal){
                return 1;
            } else if (itVal < otVal){
                return -1;
            }
        }
        
        return 0;

    }

    /**
     *
     * @return a string describing the hand in human terms, i.e.,
     * something like "2 of Hearts, 3 of Diamonds,..."
     *
     */
    @Override
    public String toString(){
        String s = "";


        //get the cards
        for (Iterator<Card> it = hand.listIterator(); it.hasNext(); ){
            s += it.next().toString();

            //add a comma
            if (it.hasNext()){
                s += ", ";
            }
        }

        s += ". ";

        //get the hand ranking
        for (HandTypes h : HandTypes.values()){
            if (h.getHandType() == handEval.get(0)){
                s += h.toString();
            }
        }

        return s;



    }

   


    /*
     * GETTERS
     */

    /**
     *
     * @return the hand
     */
    public List<Card> getHand(){
        return new ArrayList<Card>(hand);
    }

    /**
     *
     * @return the list of hand evaluations, where
     * <handType, cardval1, cardval2, highCard>
     *
     * Evaluate in the order:
     * handType, then cardval1, then cardval2, then highCard.
     *
     *
     */
    public List<Integer> getEval(){
        return new ArrayList<Integer>(handEval);
    }

    /**
     *
     * @return the hand as a map from card values to frequency. Suite not included.
     */
    public Map<Integer, Integer> asMap(){
        return new HashMap<Integer,Integer>(freqMap);
    }



    private List<Card> hand; //the list containing the elements of the hand, ie cards
    private List<Integer> handEval; //for evaluating hands against eachother
    private Map<Integer, Integer> freqMap; //stores the freq of each card

    /**
     * Constructs a hand with the list of cards
     * @param cards a list of cards
     */
    public FiveCardHand(List<Card> cards){
        this.hand = new ArrayList<Card>(cards); //safe copy
        freqMap = freqMapInit(); //init the frequency map
        handEvaluator(); //set the evaluation list

    }
    
    /*
     * create a map of each card to its frequency
     * 
     */
    private Map<Integer, Integer> freqMapInit(){
        Map<Integer, Integer> fmap = new HashMap<Integer, Integer>();
        Integer freq;
        
        /*
         * for each card in hand, set the frequency to one if it is not yet in
         * the hand, or add one to the frequency
         */
        for (Card c : hand){
            freq = fmap.get(c.getValue());
            fmap.put(c.getValue(), (freq == null) ? 1 : freq + 1);
        }

        return fmap;
        
        
    }



    /*
     * Check to see if flush. 
     */
    private boolean flushCheck(){
        int suite = hand.get(0).getSuite(); //all suite must = this
        

        for (Card c : hand){
            if (c.getSuite() != suite){
                return false;
            }
        }

        return true;


    }

    /*
     * Check to see if all royal.
     */
    private boolean royalCheck(){

        for (ListIterator<Card> it = hand.listIterator(); it.hasNext();){
            if (it.next().getValue() < 10){
                return false;
            }
        }

        return true;
    }
    


    //Evaluates and assigns a value to the hand and rank to the hand. Value is
    //initial check to compare hands, then rank. I.e., a four of a kind is better
    //than two pair, but if both hands have two pair, their rank determines winner.
   
    private void handEvaluator(){



        /*
         * Note that order is important here! Two pair is also two kind, etc.
         */

        if (four_kind()){
        } else if (full_house()){
        } else if (three_kind()){
        } else if (two_pair()){
        } else if (two_kind()){
        } else if (royal_flush()){
        } else if (straight_flush()){
        } else if (straight()){
        } else if (flush()){
        } else {high_card();}

    }
    
    
    /*
     * Methods to determine the value of a card hand and its corresponding rank.
     *
     *
     */

    private void high_card(){
        int handType = HandTypes.HIGH_CARD.getHandType();
        int cardValue1 = Collections.max(hand).getValue();
        int cardValue2 = 0; //none
        int highCard = 0; //none

        handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);
    }

    


    
    private boolean four_kind(){
        int handType = -1;
        int cardValue1 = -1;
        int cardValue2 = 0; //four of a kind doesnt have a second ranking
        int highCard = -1;

        if (freqMap.containsValue(4)){ //if its a 4 of a kind

            handType = HandTypes.FOUR_KIND.getHandType();

            //iterate over entry sets to find the card value
            for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()){

                //find the cards
                if (entry.getValue() == 4){
                    cardValue1 = entry.getKey();
                }

                //find the high card
                if (entry.getKey() > highCard){
                    highCard = entry.getKey();
                }


            }

            //set the hand evaluation
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);

            return true;


        }

        return false;


       
    }




    private boolean full_house(){
        int handType = -1;
        int cardValue1 = -1;
        int cardValue2 = -1;
        int highCard = 0; //no high card

        if (freqMap.containsValue(3) && freqMap.containsValue(2)){
            handType = HandTypes.FULL_HOUSE.getHandType();

            //iterate over entry sets to find the card value
            for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()){

                //find the cards
                if (entry.getValue() == 3){
                    cardValue1 = entry.getKey();
                }

                if (entry.getValue() == 2){
                    cardValue2 = entry.getKey();
                }

              


            }

             //set the hand evaluation
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);

            return true;

        }

        return false;


        
    }

    private boolean three_kind(){
        int handType = -1;
        int cardValue1 = -1;
        int cardValue2 = 0; //no second card value
        int highCard = -1;

         if (freqMap.containsValue(3)){ //if its a 4 of a kind

            handType = HandTypes.THREE_KIND.getHandType();

            //iterate over entry sets to find the card value
            for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()){

                //find the cards
                if (entry.getValue() == 3){
                    cardValue1 = entry.getKey();
                }

                //find the high card
                if (entry.getKey() > highCard){
                    highCard = entry.getKey();
                }


            }

            //set the hand evaluation
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);

            return true;


        }

        return false;



        

    }


    private boolean two_pair(){
        int handType = -1;
        int cardValue1 = -1;
        int cardValue2 = -1;
        int highCard = -1;

        if (Collections.frequency(freqMap.values(), 2) == 2){
            handType = HandTypes.TWO_PAIR.getHandType();

            //iterate over entry sets to find the card value
            for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()){

                //find the cards
                if (entry.getValue() == 2){

                    if (cardValue1 == -1){
                        cardValue1 = entry.getKey();
                    } else{
                        cardValue2 = entry.getKey();
                    }
                }

               

                //find the high card
                if (entry.getKey() > highCard){
                    highCard = entry.getKey();
                }


            }

            //cardValue1 must be > than cardValue2, fixes it
            if (cardValue2 > cardValue1){
                int temp = cardValue2;
                cardValue2 = cardValue1;
                cardValue1 = temp;
            }

             //set the hand evaluation
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);

            return true;

        }

        return false;



    }


    private boolean two_kind(){
         int handType = -1;
        int cardValue1 = -1;
        int cardValue2 = 0; //no second card value
        int highCard = -1;

         if (freqMap.containsValue(2)){ //if its a 2 of a kind

            handType = HandTypes.TWO_KIND.getHandType();

            //iterate over entry sets to find the card value
            for (Map.Entry<Integer, Integer> entry : freqMap.entrySet()){

                //find the cards
                if (entry.getValue() == 2){
                    cardValue1 = entry.getKey();
                }

                //find the high card
                if (entry.getKey() > highCard){
                    highCard = entry.getKey();
                }


            }

            //set the hand evaluation
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);

            return true;


        }

        return false;

        
    }

    private boolean royal_flush(){
        int handType = HandTypes.ROYAL_FLUSH.getHandType();
        int cardValue1 = 0; //no first value
        int cardValue2 = 0; //no second value
        int highCard = 0; // no high card
        
        if (royalCheck() && flushCheck()){
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);
            return true;
        }
        
        return false;


        

    }

    private boolean straight_flush(){
        int handType = HandTypes.STRAIGHT_FLUSH.getHandType();
        int cardValue1 = -1;
        int cardValue2 = 0; //no second value
        int highCard = 0; // no high card

        if (flushCheck() && straightCheck()){

            cardValue1 = Collections.max(hand).getValue();
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);
            return true;
        }

        return false;
    }

    private boolean straight(){
        int handType = HandTypes.STRAIGHT.getHandType();
        int cardValue1 = -1;
        int cardValue2 = 0; //no second value
        int highCard = 0; // no high card

        if (straightCheck()){

            cardValue1 = Collections.max(hand).getValue();
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);
            return true;
        }

        return false;
        
        

    }

    private boolean straightCheck(){

        Collections.sort(hand); //sort the hand
        int previousVal = hand.get(0).getValue() - 1; //init

        for (Card c : hand){
            if (c.getValue() != previousVal + 1){
                return false;
            }

            previousVal = c.getValue();
        }

        return true;


    }

    private boolean flush(){
        int handType = HandTypes.FLUSH.getHandType();
        int cardValue1 = -1;
        int cardValue2 = 0; //no second value
        int highCard = 0; // no high card

        if (flushCheck()){
            cardValue1 = Collections.max(hand).getValue();
            handEval = Arrays.asList(handType, cardValue1, cardValue2, highCard);
            return true;
        }

        return false;


    }

     private static enum HandTypes{
        HIGH_CARD(1),
        TWO_KIND(2),
        TWO_PAIR(3),
        THREE_KIND(4),
        STRAIGHT(5),
        FLUSH(6),
        FULL_HOUSE(7),
        FOUR_KIND(8),
        STRAIGHT_FLUSH(9),
        ROYAL_FLUSH(10);


        private final int handType;

        private HandTypes(int typeValue){
            this.handType = typeValue;
        }

        int getHandType(){
            return handType;
        }

    }












}
